﻿#!/usr/bin/python
# -*- coding: utf-8 -*-

''' parserSLA.py

Module permettant de parser après avoir effectué un scan.

2009-2010, Elias Capito et Jonathan Neuhaus, HE-Arc

'''

import ply.yacc as yacc
from lexerSLA import tokens
import AST as AST

# -------- Liste des priorités -------- 
precedence = (
	('left', 'ADD_OP'),
	('left', 'MUL_OP'),
	('left', 'SHIFT'),
	('left', 'EQUALITY'),
	('left', 'COMPARE'),
	('left', 'BINARY'),
	('right', 'UMINUS'), 
	('right', '~')
)

# -------- Programme -------- 
def p_programme_statement(p):
	''' programme : statement
		| condition
		| structure
		| funcDef
		| select '''
	p[0] = AST.ProgramNode(p[1])

def p_programme_recursive(p):
	''' programme : statement NEWLINE programme'''
	p[0] = AST.ProgramNode([p[1]]+p[3].children)

def p_programme_newline(p):
	''' programme : NEWLINE programme '''
	p[0] = p[2]

def p_programme_bloc(p):
	''' programme : condition programme
		| structure programme
		| funcDef programme
		| select programme
		'''
	p[0] = AST.ProgramNode([p[1]]+p[2].children)

# -------- Générals -------- 
def p_generals(p):
	'''generals : NUMBER
		| IDENTIFIER 
		| SINGLE_QUOTE '''
	p[0] = AST.TokenNode(p[1])

def p_generals_string(p):
 	'''string : DOUBLE_QUOTES '''
	p[0] = AST.StringNode(p[1])
	
def p_indent(p):
	''' indent : INDENT 
		| NEWLINE indent '''

# -------- Statement -------- 
def p_statement(p):
	''' statement : assignation	
		| funcUse 
		| configDir
		| configPullUp
		| configUSART 
		| configPWMT0
		| enable
		| disable
		| write
		| flip
		| read'''
	p[0] = p[1]

def p_statement_return(p):
	''' statement : RETURN expression '''
	p[0] = AST.ReturnNode(p[2])
	
def p_statement_break(p):
	'''statement : BREAK'''
	p[0] = AST.BreakNode(None)

# -------- Structure de boucle for, while -------- 
def p_structure_loop(p):
	''' structure : LOOP '(' test ')' indent programme DEDENT 
		| LOOP '(' ')' indent programme DEDENT '''
	if len(p) <= 7:
		p[0] = AST.LoopNode([AST.TokenNode("1"),p[5]])
	else:
		p[0] = AST.LoopNode([p[3],p[6]])

def p_structure_for(p):
	''' structure : FOR '(' assignation ';' test ';' assignation ')' indent programme DEDENT '''
	p[0] = AST.ForNode([p[3],p[5],p[7],p[10]])

# -------- Condition test, if, else if, else -------- 
def p_condition(p):
	'''condition : if 
		| elif
		| else'''
	p[0] = p[1]

def p_test(p):
	''' test : expression COMPARE expression
		| expression EQUALITY expression 
		| expression BINARY expression 
		| generals
		| read '''
	if len(p)<=2:
		p[0] = p[1]
	else:
		p[0] = AST.OpNode(p[2], [p[1], p[3]])
		
def p_if(p):
	''' if : IF '(' test ')' indent programme DEDENT'''
	p[0] = AST.IfNode([p[3], p[6]])
	
def p_elif(p):
	''' elif : if ELIF '(' test ')' indent programme DEDENT
			| elif ELIF '(' test ')' indent programme DEDENT'''
	p[0] = AST.ElifNode([p[1], p[4], p[7]])

def p_else(p):
	''' else : if ELSE indent programme DEDENT
			| elif ELSE indent programme DEDENT'''
	p[0] = AST.ElseNode([p[1],p[4]])	

# -------- Switch Case -------- 
def p_select(p):
	''' select : SELECT '(' expression ')' indent case DEDENT'''
	p[0] = AST.SelectNode([p[3]] + p[6].children)		

def p_case(p):
	''' case : CASE expression indent programme DEDENT 
		| case CASE expression indent programme DEDENT '''
	if len(p) <= 6:
		p[0] = AST.CaseNode([p[2], p[4]])
	else:
		tmp = AST.CaseNode([p[3], p[5]])
		p[0] = AST.CaseNode([p[1], tmp])

# -------- Fonction définition, utilisation -------- 
def p_func_def(p):
	''' funcDef : FUNC IDENTIFIER '(' ')' indent programme DEDENT 
		| FUNC IDENTIFIER '(' paramsDef ')' indent programme DEDENT'''
	if len(p) == 8:
		p[0] = AST.FuncDefNode(p[2], "", p[6])
	else:
		p[0] = AST.FuncDefNode(p[2], p[4], p[7])

def p_func_use(p):
	''' funcUse : IDENTIFIER '(' ')' 
		| IDENTIFIER '(' paramsUse ')' '''
	if len(p) == 4:
		p[0] = AST.FuncUseNode(p[1], None)
	else:
		p[0] = AST.FuncUseNode(p[1], p[3])

# -------- Paramètres pour fonction définition, utilisation -------- 
def p_params_def(p):
	''' paramsDef : IDENTIFIER 
		| paramsDef ';' IDENTIFIER ''' 
	if len(p)==2:
		p[0] = [p[1]]
	else:
		params = eval(str(p[1]))
		params.append(p[3])
		p[0] = params

def p_params_use_expression(p):
	''' paramsUse : expression
		| paramsUse ';' expression ''' 
	if len(p)==2:
		p[0] = AST.ParamsUseNode(p[1])
	else:
		p[0] = AST.ParamsUseNode(p[1].children + [p[3]])
		
def p_params_use_string(p):
	''' paramsUse : string
		| paramsUse ';' string ''' 
	if len(p)==2:
		p[0] = AST.ParamsUseNode(p[1])
	else:
		p[0] = AST.ParamsUseNode(p[1].children + [p[3]])

# -------- Expression -------- 
def p_expression_op(p):
	''' expression : expression ADD_OP expression
			| expression MUL_OP expression
			| expression SHIFT expression '''
	p[0] = AST.OpNode(p[2], [p[1], p[3]])

def p_minus(p):
	''' expression : ADD_OP expression %prec UMINUS
		| '~' expression %prec '~'  '''
	p[0] = AST.OpNode(p[1], p[2])
	
def p_expression_test(p):
	''' expression : test 
		| funcUse '''
	p[0] = p[1]

def p_expression_parentheses(p):
	''' expression : '(' expression ')' '''
	p[0] = AST.ParenthesesNode(p[2])

# -------- Assignation -------- 
def p_assign(p):
	''' assignation : IDENTIFIER '=' expression '''
	p[0] = AST.AssignNode([AST.TokenNode(p[1]),p[3]])

# -------- Opération de bit 
def p_enable(p):
	''' enable : ENABLE PIN 
		| ENABLE IDENTIFIER
		| ENABLE IDENTIFIER NUMBER '''
	if(len(p) <=3):
		p[0] = AST.EnableNode([p[2]])
	else:
		p[0] = AST.EnableNode([p[2],p[3]])

def p_disable(p):
	''' disable : DISABLE PIN 
		| DISABLE IDENTIFIER
		| DISABLE IDENTIFIER NUMBER '''
	if(len(p) <=3):
		p[0] = AST.DisableNode([p[2]])
	else:
		p[0] = AST.DisableNode([p[2],p[3]])

def p_write(p):
	''' write : WRITE PIN generals
		| WRITE IDENTIFIER generals
		| WRITE IDENTIFIER NUMBER generals'''
	if(len(p) <=4):
		p[0] = AST.WriteNode([p[2],p[3]])
	else:
		p[0] = AST.WriteNode([p[2],p[3],p[4]])

def p_flip(p):
	''' flip : FLIP PIN 
		| FLIP IDENTIFIER
		| FLIP IDENTIFIER NUMBER '''
	if(len(p) <=3):
		p[0] = AST.FlipNode([p[2]])
	else:
		p[0] = AST.FlipNode([p[2],p[3]])		

def p_read(p):
	''' read : READ PIN 
		| READ IDENTIFIER
		| READ IDENTIFIER NUMBER '''
	if len(p) <=3:
		p[0] = AST.ReadNode([p[2]])
	else:
		p[0] = AST.ReadNode([p[2],p[3]])

# -------- Propre à ATMEL -------- 
def p_configDir(p):
	''' configDir : CONFIGDIR IN PIN
		| CONFIGDIR OUT PIN '''
	p[0] = AST.ConfigDirNode(p[2],p[3])

def p_configPullUp(p):
	''' configPullUp : CONFIGPULLUP ON PIN
		| CONFIGPULLUP OFF PIN '''
	p[0] = AST.ConfigPullUpNode(p[2],p[3])

def p_configUSART(p):
	''' configUSART : CONFIGUSART NUMBER NUMBER NUMBER NUMBER '''
	p[0] = AST.ConfigUSARTNode(p[2],p[3],p[4],p[5])

def p_configPWMT0(p):
	''' configPWMT0 : CONFIGPWMT0 NUMBER '''
	p[0] = AST.ConfigPWMT0Node(p[2])

# Gestion d'erreur simple
def p_error(p):
	if p:
		print "Syntax erreur a la ligne %d" % p.lineno
		yacc.errok()
	else:
		print "Syntax erreur: fin de fichier inatendu !"

# Fonction permettant de parser 
def parserSLA(program):
	from lexerSLA import scanSLA
	lexerSLA = scanSLA(program)
	return yacc.parse(program, debug=0, lexer=lexerSLA)

yacc.yacc(outputdir='generated')

if __name__ == "__main__":
	import sys 
	
	try:
		prog = file(sys.argv[1]).read()
		
		result = parserSLA(prog)
		if result:
			print result
			import os
			graph = result.makegraphicaltree()
			name = os.path.splitext(sys.argv[1])[0]+'-ast.pdf'
			graph.write_pdf(name) 
			print "L'arbre syntaxique a ete genere sous ce nom: ", name
		else:
			print "*** ERREUR : Le parserSLA n'a retourne aucun resultat ***"
	except IOError:
		print "*** ERREUR : Le module parserSLA n'a pas trouve le fichier ***"	